当 Redis 发生高延迟时,到底发生了什么
The following article is from 程序员历小冰 Author 历小冰
(给ImportNew加星标,提高Java技能)
转自:博客园 作者:程序员小冰
www.cnblogs.com/remcarpediem/p/11845065.html
Redis 是一种内存数据库,将数据保存在内存中,读写效率要比传统的将数据保存在磁盘上的数据库要快很多。但是 Redis 也会发生延迟时,这是就需要我们对其产生原因有深刻的了解,以便于快速排查问题,解决 Redis的延迟问题
一条命令执行过程
在本文场景下,延迟 (latency) 是指从客户端发送命令到客户端接收到命令返回值的时间间隔。所以我们先来看一下 Redis 一条命令执行的步骤,其中每个步骤出问题都可能导致高延迟。
上图是 Redis 客户端发送一条命令的执行过程示意图,绿色的是执行步骤,而蓝色的则是可能出现的导致高延迟的原因。
网络连接限制、网络传输速率和CPU性能等是所有服务端都可能产生的性能问题。但是 Redis 有自己独有的可能导致高延迟的问题:命令或者数据结构误用、持久化阻塞和内存交换。
而且更为致命的是,Redis 采用单线程和事件驱动的机制来处理网络请求,分别有对应的连接应答处理器,命令请求处理器和命令回复处理器来处理客户端的网络请求事件,处理完一个事件就继续处理队列中的下一个。一条命令处理出现了高延迟会影响接下来处于排队状态的其他命令。
对于高延迟,Redis 原生提供慢查询统计功能,执行 slowlog get {n} 命令可以获取最近的 n 条慢查询命令,默认对于执行超过10毫秒(可配置)的命令都会记录到一个定长队列中,线上实例建议设置为1毫秒便于及时发现毫秒级以上的命令。
# 超过 slowlog-log-slower-than 阈值的命令都会被记录到慢查询队列中
# 队列最大长度为 slowlog-max-len
slowlog-log-slower-than 10000
slowlog-max-len 128
如果命令执行时间在毫秒级,则实例实际OPS只有1000左右。慢查询队列长度默认128,可适当调大。慢查询本身只记录了命令执行时间,不包括数据网络传输时间和命令排队时间,因此客户端发生阻塞异常 后,可能不是当前命令缓慢,而是在等待其他命令执行。需要重点比对异常和慢查询发生的时间点,确认是否有慢查询造成的命令阻塞排队。
slowlog的输出格式如下所示。第一个字段表示该条记录在所有慢日志中的序号,最新的记录被展示在最前面;第二个字段是这条记录被记录时的系统时间,可以用 date 命令来将其转换为友好的格式第三个字段表示这条命令的响应时间,单位为 us (微秒);第四个字段为对应的 Redis 操作。
> slowlog get
1) 1) (integer) 26
2) (integer) 1450253133
3) (integer) 43097
4) 1) "flushdb"
不合理的命令或者数据结构
> redis-cli -h host -p 12345 --bigkeys
# Scanning the entire keyspace to find biggest keys as well as
# average sizes per key type. You can use -i 0.1 to sleep 0.1 sec
# per 100 SCAN commands (not usually needed).
[00.00%] Biggest hash found so far 'idx:user' with 1 fields
[00.00%] Biggest hash found so far 'idx:product' with 3 fields
[00.00%] Biggest hash found so far 'idx:order' with 14 fields
[02.29%] Biggest hash found so far 'idx:fund' with 16 fields
[02.29%] Biggest hash found so far 'idx:pay' with 69 fields
[04.45%] Biggest set found so far 'indexed_word_set' with 1482 members
[05.93%] Biggest hash found so far 'idx:address' with 159 fields
[11.79%] Biggest hash found so far 'idx:reply' with 196 fields
-------- summary -------
Sampled 1484 keys in the keyspace!
Total key length in bytes is 13488 (avg len 9.09)
Biggest set found 'indexed_word_set' has 1482 members
Biggest hash found 'idx:的' has 196 fields
0 strings with 0 bytes (00.00% of keys, avg size 0.00)
0 lists with 0 items (00.00% of keys, avg size 0.00)
2 sets with 1710 members (00.13% of keys, avg size 855.00)
1482 hashs with 6731 fields (99.87% of keys, avg size 4.54)
0 zsets with 0 members (00.00% of keys, avg size 0.00)
> redis-cli -c -p 7000 info | grep -w latest_fork_usec
latest_fork_usec:315
Asynchronous AOF fsync is taking too long (disk is busy). \
Writing the AOF buffer without waiting for fsync to complete, \
this may slow down Redis.
也可以查看 info persistence 统计中的 aofdelayedfsync 指标,每次发生 fdatasync 阻塞主线程时会累加。
>info persistence
loading:0
aof_pending_bio_fsync:0
aof_delayed_fsync:0
内存交换
>redis-cli -p 6383 info server | grep process_id # 查询 redis 进程号
>cat /proc/4476/smaps | grep Swap # 查询内存交换大小
Swap: 0 kB
Swap: 4 kB
Swap: 0 kB
Swap: 0 kB
保证机器充足的可用内存 确保所有Redis实例设置最大可用内存(maxmemory),防止极端情况下 Redis 内存不可控的增长。 降低系统使用swap优先级,如 echo10>/proc/sys/vm/swappiness。
看完本文有收获?请转发分享给更多人
关注「ImportNew」,提升Java技能
好文章,我在看❤️